﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using System.Security.Principal;

namespace IKYSWeb.Siniflar
{
    [Serializable]
    public class DosyaErisimAraci : IDisposable
    {
        #region Private değişkenler
        string BilgisayarAdi, DosyaAdresi, KullaniciAdi, Sifre;

        IntPtr token = IntPtr.Zero;
        IntPtr dupToken = IntPtr.Zero;

        private FileStream fsDosya;

        WindowsImpersonationContext impersonationContext;

        #endregion

        #region Dll Import

        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool LogonUser(string
                lpszUsername, string lpszDomain, string lpszPassword,
                int dwLogonType, int dwLogonProvider, ref
                IntPtr phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private unsafe static extern int FormatMessage(int
                dwFlags, ref IntPtr lpSource,
                int dwMessageId, int dwLanguageId, ref String
                lpBuffer, int nSize, IntPtr* arguments);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool CloseHandle(IntPtr handle);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public extern static bool DuplicateToken(IntPtr
                existingTokenHandle,
                int SECURITY_IMPERSONATION_LEVEL, ref IntPtr
                duplicateTokenHandle);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool RevertToSelf();

        #endregion

        #region Const değişkenler

        // logon types
        const int LOGON32_LOGON_INTERACTIVE = 2;
        const int LOGON32_LOGON_NETWORK = 3;
        const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

        // logon providers
        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_PROVIDER_WINNT50 = 3;
        const int LOGON32_PROVIDER_WINNT40 = 2;
        const int LOGON32_PROVIDER_WINNT35 = 1;

        #endregion

        #region Public Özellikler

        public FileStream FSDosya
        {
            get { return fsDosya; }
        }

        #endregion

        public DosyaErisimAraci(string BilgisayarAdi, string DosyaAdresi, string KullaniciAdi, string Sifre)
        {
            this.BilgisayarAdi = BilgisayarAdi;
            this.DosyaAdresi = DosyaAdresi;
            this.KullaniciAdi = KullaniciAdi;
            this.Sifre = Sifre;

            ////Baştaki \\ sil
            //if (BilgisayarAdi.StartsWith("\\\\"))
            //    BilgisayarAdi = BilgisayarAdi.Remove(0, 2);

            ////Sona \\ ekle
            //if (!BilgisayarAdi.EndsWith("\\"))
            //    BilgisayarAdi += "\\";

        }

        public bool YoksaKlasorOlustur(string KlasorYolu)
        {
            if (Impersonate())
            {
                if (!Directory.Exists(KlasorYolu))
                {
                    Directory.CreateDirectory(KlasorYolu);
                    return true;
                }
                return true;
            }
            return false;
        }

        public bool DosyaOku()
        {
            if (Impersonate())
            {
                fsDosya = File.OpenRead(DosyaAdresi);
                return true;
            }
            return false;
        }

        public bool DosyaOlustur(byte[] dosyaIcerik)
        {
            if (Impersonate())
            {
                File.WriteAllBytes(DosyaAdresi, dosyaIcerik);

                return true;
            }
            return false;
        }

        public bool DosyaSil()
        {
            if (Impersonate())
            {
                if (File.Exists(DosyaAdresi))
                {
                    File.Delete(DosyaAdresi);
                    return true;
                }
            }

            return false;
        }

        private bool Impersonate()
        {
            //if (System.Environment.UserName == KullaniciAdi)
            //    return true;

            WindowsIdentity tempWindowsIdentity;
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

            if (RevertToSelf())
            {

                bool isSuccess = LogonUser(KullaniciAdi, BilgisayarAdi, Sifre, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref token);
                
                if (!isSuccess)
                {
                    RaiseLastError();
                }

                if (DuplicateToken(token, 2, ref tokenDuplicate))
                {
                    tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();

                    if (impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);

                        return true;
                    }
                }
            }

            if (token != IntPtr.Zero)
                CloseHandle(token);
            if (tokenDuplicate != IntPtr.Zero)
                CloseHandle(tokenDuplicate);

            return false;
        }

        // corresponding to the input errorCode.
        public unsafe static string GetErrorMessage(int errorCode)
        {
            int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
            int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
            int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

            int messageSize = 255;
            string lpMsgBuf = "";
            int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;

            IntPtr ptrlpSource = IntPtr.Zero;
            IntPtr ptrArguments = IntPtr.Zero;

            int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, &ptrArguments);

            if (retVal == 0)
            {
                throw new ApplicationException(string.Format("Dosya sunucuya erişirken hata oluştu ve hata mesajı alınamadı. Hata kodu: '{0}'.", errorCode));
            }

            return lpMsgBuf;
        }

        private static void RaiseLastError()
        {
            int errorCode = Marshal.GetLastWin32Error();
            string errorMessage = GetErrorMessage(
            errorCode);

            throw new ApplicationException(errorMessage);
        }


        #region IDisposable Members

        public void Dispose()
        {
            BilgisayarAdi = null;
            DosyaAdresi = null;
            KullaniciAdi = null;
            Sifre = null;

            if (fsDosya != null)
                fsDosya.Close();

            if(impersonationContext != null)
                impersonationContext.Undo();

        }

        #endregion
    }
}
